package controllers

import (
	"archive/zip"
	"backend/common"
	"backend/config"
	"crypto/rand"
	"github.com/cloudwego/eino/schema"
	"github.com/gin-gonic/gin"
	"math/big"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"time"
)

// GenerateCode 生成项目代码
func GenerateCode(ctx *gin.Context) {
	// 解析请求
	mes := struct {
		FlowID uint `json:"workflowID" binding:"required"`
	}{}
	err := ctx.ShouldBindJSON(&mes)
	if err != nil {
		ctx.JSON(http.StatusUnprocessableEntity, gin.H{"status": "error", "message": "请确认请求格式"})
		return
	}

	// 获取工作流
	workflow, err := getDataflow(mes.FlowID)
	if err != nil {
		ctx.JSON(http.StatusUnprocessableEntity, gin.H{"status": "error", "message": "没有权限" + err.Error()})
		return
	}
	//验证权限
	userID, _ := getUserID(ctx)
	if workflow.UserID != userID {
		ctx.JSON(http.StatusUnprocessableEntity, gin.H{"status": "error", "message": "没有权限2"})
		return
	}

	messages := []*schema.Message{
		schema.SystemMessage("你现在是一个专业的项目开发AI，请根据我提供的JSON格式的工作流，自动生成一个完整可运行的微型应用项目。项目的语言工作流中会给出" +
			"任务目标：自动构建完整项目：所有应用需要包含用户输入、运算逻辑、后端服务（如需要）、错误处理、运行说明等。" +
			"简单应用无需包含前端，以命令行的格式输入输出" +
			"web应用、前后端分离的全栈应用、额外包括前端界面、后端服务（如需要）等。最终输出的项目应该是开箱即用的开发者项目目录结构，包含所有代码文件内容" +
			"请严格遵循以下要求：" +
			"1. 每个JSON中的dataflow节点必须对应成一个具体的处理模块，节点中的description转换为代码注释，jump_list中的条件跳转要通过if/else语句或状态控制逻辑明确实现" +
			"2. 必须有输入逻辑（如输入框、选择框、按钮）和输出逻辑（如显示计算结果或错误信息），并与流程节点精确对应" +
			"3. 如果包含后端（如Flask），则前端应通过fetch或axios调用接口，并展示接口返回结果。必须包含接口文档说明" +
			"4. 错误处理节点的处理方式：如果是输入错误，请在界面上用红色字体或弹窗提示；如果是逻辑错误（如除数为0），请清晰反馈并终止当前流程。" +
			"5. 输出格式要求如下，必须严格按照以下方式输出每个文件内容：\n==== 文件路径（如 src/App.jsx） ====\n（完整代码）\n==== 文件路径（如 pages/index/index.wxml） ====\n（完整代码）\n不要输出 markdown，不要用代码块标记，不要夹杂任何解释性文字，直接输出纯文本代码文件内容，每个文件之间用 ==== 文件路径 ==== 分隔\n" +
			"6. 必须包含 README.md 文件，内容必须包括以下内容：•\t项目简介\n•\t技术栈\n•\t依赖安装方式（npm / yarn / pip）\n•\t运行命令\n•\t本地测试方式\n•\t若为小程序，请写清模拟器运行方式或使用微信开发者工具打开方式\n" +
			"7. 必须包含整个项目所需的全部文件，例如web项目必须包含初始的index.html文件，vite项目必须包含入口点文件" +
			"以下是我要你处理的工作流JSON数据"),
		schema.UserMessage(workflow.Dataflow),
	}

	//生成回复
	response, err := common.Interact(messages)
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, gin.H{
			"status":  "error",
			"message": "服务器错误101 " + err.Error(),
		})
		return
	}

	//currentFile, err := os.Create("E:\\Programming\\BuildMind\\a.txt")
	//_, err = currentFile.Write([]byte(response.Content))
	//if err != nil {
	//	ctx.JSON(http.StatusInternalServerError, gin.H{
	//		"status":  "error",
	//		"message": err.Error(),
	//	})
	//	return
	//}
	//currentFile.Close()
	// println(response.Content)

	n, _ := rand.Int(rand.Reader, big.NewInt(90000000))
	randomNum := strconv.FormatInt(n.Int64()+10000000, 10)
	randomNum = strconv.FormatUint(uint64(userID), 10) + randomNum

	path := filepath.Join(config.Configs.CodeStorePath, randomNum)
	err = common.ParseAndSaveFiles(path, response.Content)
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, gin.H{
			"status":  "error",
			"message": "服务器错误102 " + err.Error(),
		})
		return
	}

	pathList := make([]string, 0)
	pathList, err = common.WalkDir(path, path, pathList)
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, gin.H{
			"status":  "error",
			"message": err.Error(),
		})
		return
	}

	err = insertFileID(userID, randomNum)
	if err != nil {
		return
	}

	ctx.JSON(http.StatusOK, gin.H{
		"status":  "success",
		"message": "项目代码生成成功",
		"data": struct {
			FileNo   string   `json:"fileNo"`
			PathList []string `json:"pathList"`
		}{randomNum, pathList},
	})
	return
}

func GetFileList(ctx *gin.Context) {
	// 解析请求
	fileNo := ctx.Query("fileNo")
	if fileNo == "" {
		ctx.JSON(http.StatusUnprocessableEntity, gin.H{"status": "error", "message": "请确认请求格式"})
		return
	}

	//验证权限
	userID, _ := getUserID(ctx)
	check, _ := checkFileID(userID, fileNo)
	if check != true {
		ctx.JSON(http.StatusUnprocessableEntity, gin.H{"status": "error", "message": "文件路径编号错误"})
		return
	}

	pathList := make([]string, 0)
	path := filepath.Join(config.Configs.CodeStorePath, fileNo)
	pathList, err := common.WalkDir(path, path, pathList)
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, gin.H{
			"status":  "error",
			"message": err.Error(),
		})
		return
	}

	ctx.JSON(http.StatusOK, gin.H{
		"status": "success",
		"data": struct {
			PathList []string `json:"pathList"`
		}{pathList},
	})
	return
}

func GetFile(ctx *gin.Context) {
	// 解析请求
	fileNo := ctx.Query("fileNo")
	filePath := ctx.Query("filePath")
	if fileNo == "" || filePath == "" {
		ctx.JSON(http.StatusUnprocessableEntity, gin.H{"status": "error", "message": "请确认请求格式"})
		return
	}
	path := filepath.Join(config.Configs.CodeStorePath, fileNo, filePath)

	//验证权限
	userID, _ := getUserID(ctx)
	check, _ := checkFileID(userID, fileNo)
	if check != true {
		ctx.JSON(http.StatusUnprocessableEntity, gin.H{"status": "error", "message": "文件路径编号错误"})
		return
	}

	content, err := os.ReadFile(path)
	if err != nil {
		statusCode := http.StatusInternalServerError
		message := err.Error()

		if os.IsNotExist(err) {
			statusCode = http.StatusNotFound
			message = "文件不存在"
		} else if os.IsPermission(err) {
			statusCode = http.StatusForbidden
			message = "无权限访问文件"
		}

		ctx.JSON(statusCode, gin.H{
			"status":  "error",
			"message": message,
		})
		return
	}
	ctx.JSON(http.StatusOK, gin.H{
		"status": "success",
		"data": struct {
			Content string `json:"content"`
		}{string(content)},
	})
	return
}

func ChangeFile(ctx *gin.Context) {
	// 解析请求
	mes := struct {
		FileNo   string `json:"fileNo" binding:"required"`
		FilePath string `json:"filePath" binding:"required"`
		Content  string `json:"content" binding:"required"`
	}{}
	err := ctx.ShouldBindJSON(&mes)
	if err != nil {
		ctx.JSON(http.StatusUnprocessableEntity, gin.H{"status": "error", "message": "请确认请求格式"})
		return
	}

	//验证权限
	userID, _ := getUserID(ctx)
	check, _ := checkFileID(userID, mes.FileNo)
	if check != true {
		ctx.JSON(http.StatusUnprocessableEntity, gin.H{"status": "error", "message": "文件路径编号错误"})
		return
	}

	path := filepath.Join(config.Configs.CodeStorePath, mes.FileNo, mes.FilePath)
	path = filepath.Clean(path)
	absPath, err := filepath.Abs(path)

	if err != nil {
		ctx.JSON(http.StatusUnprocessableEntity, gin.H{"status": "error", "message": "无法访问路径"})
		return
	}
	if fileInfo, err := os.Stat(absPath); err != nil {
		if os.IsNotExist(err) {
			ctx.JSON(http.StatusUnprocessableEntity, gin.H{"status": "error", "message": "文件不存在"})
			return
		}
		if fileInfo.IsDir() {
			ctx.JSON(http.StatusUnprocessableEntity, gin.H{"status": "error", "message": "指定路径是目录而不是文件"})
			return
		}
		ctx.JSON(http.StatusUnprocessableEntity, gin.H{"status": "error", "message": "无法访问路径"})
		return
	}

	// 检查文件是否可写
	file, err := os.OpenFile(absPath, os.O_WRONLY, 0644)
	if err != nil {
		ctx.JSON(http.StatusUnprocessableEntity, gin.H{"status": "error", "message": "文件不可写"})
		return
	}
	err = file.Close()
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, gin.H{"status": "error", "message": err.Error()})
		return
	}

	// 写入文件内容
	err = os.WriteFile(absPath, []byte(mes.Content), 0644)
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, gin.H{"status": "error", "message": "文件写入失败"})
		return
	}

	ctx.JSON(http.StatusOK, gin.H{"status": "success", "message": "文件修改成功"})
}

func DownloadProject(ctx *gin.Context) {
	fileNo := ctx.Query("fileNo")
	if fileNo == "" {
		ctx.JSON(http.StatusUnprocessableEntity, gin.H{"status": "error", "message": "请确认请求格式"})
		return
	}

	//验证权限
	userID, _ := getUserID(ctx)
	check, _ := checkFileID(userID, fileNo)
	if check != true {
		ctx.JSON(http.StatusUnprocessableEntity, gin.H{"status": "error", "message": "文件路径编号错误"})
		return
	}

	path := filepath.Join(config.Configs.CodeStorePath, fileNo)
	path = filepath.Clean(path)
	absPath, err := filepath.Abs(path)
	if err != nil {
		ctx.JSON(http.StatusUnprocessableEntity, gin.H{"status": "error", "message": "无法访问路径"})
		return
	}
	if _, err := os.Stat(absPath); err != nil {
		if os.IsNotExist(err) {
			ctx.JSON(http.StatusUnprocessableEntity, gin.H{"status": "error", "message": "文件不存在"})
			return
		}
		ctx.JSON(http.StatusUnprocessableEntity, gin.H{"status": "error", "message": "无法访问路径"})
		return
	}
	filename := "download_" + time.Now().Format("20060102_150405") + ".zip"

	ctx.Header("Content-Disposition", "attachment; filename="+filename)
	ctx.Header("Content-Type", "application/zip")

	zipWriter := zip.NewWriter(ctx.Writer)
	defer zipWriter.Close()

	// 添加文件到ZIP
	err = common.AddFilesToZip(zipWriter, absPath)
	if err != nil {
		ctx.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
			"status":  "error",
			"message": "打包文件失败: " + err.Error(),
		})
		return
	}

}

func DownloadSingleFile(ctx *gin.Context) {
	// 解析请求
	fileNo := ctx.Query("fileNo")
	filePath := ctx.Query("filePath")
	if fileNo == "" || filePath == "" {
		ctx.JSON(http.StatusUnprocessableEntity, gin.H{"status": "error", "message": "请确认请求格式"})
		return
	}

	//验证权限
	userID, _ := getUserID(ctx)
	check, _ := checkFileID(userID, fileNo)
	if check != true {
		ctx.JSON(http.StatusUnprocessableEntity, gin.H{"status": "error", "message": "文件路径编号错误"})
		return
	}

	path := filepath.Join(config.Configs.CodeStorePath, fileNo, filePath)

	path = filepath.Clean(path)
	absPath, err := filepath.Abs(path)
	if err != nil {
		ctx.JSON(http.StatusUnprocessableEntity, gin.H{"status": "error", "message": "无法访问路径"})
		return
	}
	if fileInfo, err := os.Stat(absPath); err != nil {
		if os.IsNotExist(err) {
			ctx.JSON(http.StatusUnprocessableEntity, gin.H{"status": "error", "message": "文件不存在"})
			return
		}
		if fileInfo.IsDir() {
			ctx.JSON(http.StatusUnprocessableEntity, gin.H{"status": "error", "message": "指定路径是目录而不是文件"})
			return
		}
		ctx.JSON(http.StatusUnprocessableEntity, gin.H{"status": "error", "message": "无法访问路径"})
		return
	}

	filename := "download_" + time.Now().Format("20060102_150405") + ".zip"

	ctx.Header("Content-Disposition", "attachment; filename="+filename)
	ctx.Header("Content-Type", "application/zip")

	zipWriter := zip.NewWriter(ctx.Writer)
	defer zipWriter.Close()

	// 添加文件到ZIP
	err = common.AddFileToZip(zipWriter, absPath)
	if err != nil {
		ctx.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
			"status":  "error",
			"message": "打包文件失败: " + err.Error(),
		})
		return
	}

}
